#define MAX_DIRECTMAP_MMU_QUEUE 130
mmu_update_t u[MAX_DIRECTMAP_MMU_QUEUE], *w, *v;
- if ( domid != 0 )
- {
- u[0].ptr = MMU_EXTENDED_COMMAND;
- u[0].val = MMUEXT_SET_FOREIGNDOM;
- u[0].val |= (unsigned long)domid << 16;
- v = w = &u[1];
- }
- else
- {
- v = w = &u[0];
- }
+ u[0].ptr = MMU_EXTENDED_COMMAND;
+ u[0].val = MMUEXT_SET_FOREIGNDOM;
+ u[0].val |= (unsigned long)domid << 16;
+ v = w = &u[1];
start_address = address;
if (!(start_info.flags & SIF_PRIVILEGED))
return -ENXIO;
+ if (file->private_data == NULL)
+ file->private_data = (void *)(unsigned long)DOMID_IO;
+
/* DONTCOPY is essential for Xen as copy_page_range is broken. */
vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#define MAX_DIRECTMAP_MMU_QUEUE 130
mmu_update_t u[MAX_DIRECTMAP_MMU_QUEUE], *w, *v;
- if ( domid != 0 )
- {
- u[0].ptr = MMU_EXTENDED_COMMAND;
- u[0].val = MMUEXT_SET_FOREIGNDOM;
- u[0].val |= (unsigned long)domid << 16;
- v = w = &u[1];
- }
- else
- {
- v = w = &u[0];
- }
+ u[0].ptr = MMU_EXTENDED_COMMAND;
+ u[0].val = MMUEXT_SET_FOREIGNDOM;
+ u[0].val |= (unsigned long)domid << 16;
+ v = w = &u[1];
start_address = address;
if (!(start_info.flags & SIF_PRIVILEGED))
return -ENXIO;
+ if (file->private_data == NULL)
+ file->private_data = (void *)(unsigned long)DOMID_IO;
+
/* DONTCOPY is essential for Xen as copy_page_range is broken. */
vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if ( (m.addr + (m.num<<PAGE_SHIFT)) > vma->vm_end )
{ ret = -EFAULT; goto batch_err; }
- if ( m.dom != 0 )
- {
- u[0].ptr = MMU_EXTENDED_COMMAND;
- u[0].val = MMUEXT_SET_FOREIGNDOM;
- u[0].val |= (unsigned long)m.dom << 16;
- v = w = &u[1];
- }
- else
- {
- v = w = &u[0];
- }
+ u[0].ptr = MMU_EXTENDED_COMMAND;
+ u[0].val = MMUEXT_SET_FOREIGNDOM;
+ u[0].val |= (unsigned long)m.dom << 16;
+ v = w = &u[1];
p = m.arr;
addr = m.addr;
int xc_rrobin_global_get(int xc_handle, u64 *slice);
#define DOMID_SELF (0x7FF0U)
+#define DOMID_IO (0x7FF1U)
+#define DOMID_XEN (0x7FF2U)
typedef struct {
#define EVTCHNSTAT_closed 0 /* Chennel is not in use. */
mfn_to_pfn_table_start_mfn = xc_get_m2p_start_mfn( xc_handle );
live_mfn_to_pfn_table =
- mfn_mapper_map_single(xc_handle, 0x7FFFU,
+ mfn_mapper_map_single(xc_handle, DOMID_XEN,
PAGE_SIZE*1024, PROT_READ,
mfn_to_pfn_table_start_mfn );
*/
#define FOREIGNDOM (percpu_info[smp_processor_id()].foreign ? : current)
-void ptwr_init_backpointers(void);
+/* Private domain structs for DOMID_XEN and DOMID_IO. */
+static struct domain *dom_xen, *dom_io;
void arch_init_memory(void)
{
+ static void ptwr_init_backpointers(void);
+ unsigned long mfn;
+
memset(percpu_info, 0, sizeof(percpu_info));
vm_assist_info[VMASST_TYPE_writeable_pagetables].enable =
ptwr_init_backpointers;
+
+ /* Initialise to a magic of 0x55555555 so easier to spot bugs later. */
+ memset(machine_to_phys_mapping, 0x55, 4<<20);
+
+ /*
+ * Initialise our DOMID_XEN domain.
+ * Any Xen-heap pages that we will allow to be mapped will have
+ * their domain field set to dom_xen.
+ */
+ dom_xen = alloc_domain_struct();
+ atomic_set(&dom_xen->refcnt, 1);
+ dom_xen->domain = DOMID_XEN;
+
+ /*
+ * Initialise our DOMID_IO domain.
+ * This domain owns no pages but is considered a special case when
+ * mapping I/O pages, as the mappings occur at the priv of the caller.
+ */
+ dom_io = alloc_domain_struct();
+ atomic_set(&dom_io->refcnt, 1);
+ dom_io->domain = DOMID_IO;
+
+ /* M2P table is mappable read-only by privileged domains. */
+ for ( mfn = virt_to_phys(&machine_to_phys_mapping[0<<20])>>PAGE_SHIFT;
+ mfn < virt_to_phys(&machine_to_phys_mapping[1<<20])>>PAGE_SHIFT;
+ mfn++ )
+ {
+ frame_table[mfn].u.inuse.count_info = 1 | PGC_allocated;
+ frame_table[mfn].u.inuse.type_info = 1 | PGT_gdt_page; /* non-RW */
+ frame_table[mfn].u.inuse.domain = dom_xen;
+ }
}
static void __invalidate_shadow_ldt(struct domain *d)
}
-int alloc_segdesc_page(struct pfn_info *page)
+static int alloc_segdesc_page(struct pfn_info *page)
{
unsigned long *descs = map_domain_mem((page-frame_table) << PAGE_SHIFT);
int i;
if ( unlikely(!pfn_is_ram(pfn)) )
{
- if ( IS_PRIV(current) )
+ /* Revert to caller privileges if FD == DOMID_IO. */
+ if ( d == dom_io )
+ d = current;
+
+ if ( IS_PRIV(d) )
return 1;
- if ( IS_CAPABLE_PHYSDEV(current) )
- return domain_iomem_in_pfn(current, pfn);
+ if ( IS_CAPABLE_PHYSDEV(d) )
+ return domain_iomem_in_pfn(d, pfn);
MEM_LOG("Non-privileged attempt to map I/O space %08lx", pfn);
return 0;
if ( !IS_PRIV(d) )
{
- MEM_LOG("Dom %u has no privilege to set subject domain",
- d->domain);
- okay = 0;
+ switch ( domid )
+ {
+ case DOMID_IO:
+ get_knownalive_domain(e = dom_io);
+ break;
+ default:
+ MEM_LOG("Dom %u cannot set foreign dom\n", d->domain);
+ okay = 0;
+ break;
+ }
}
else
{
percpu_info[cpu].foreign = e = find_domain_by_id(domid);
if ( e == NULL )
{
- MEM_LOG("Unknown domain '%u'", domid);
- okay = 0;
+ switch ( domid )
+ {
+ case DOMID_XEN:
+ get_knownalive_domain(e = dom_xen);
+ break;
+ case DOMID_IO:
+ get_knownalive_domain(e = dom_io);
+ break;
+ default:
+ MEM_LOG("Unknown domain '%u'", domid);
+ okay = 0;
+ break;
+ }
}
}
break;
* the lock so they'll spin waiting for us.
*/
if ( unlikely(e->tot_pages++ == 0) )
- get_domain(e);
+ get_knownalive_domain(e);
list_add_tail(&page->list, &e->page_list);
reassign_fail:
return 0;
}
-void ptwr_init_backpointers(void)
+static void ptwr_init_backpointers(void)
{
struct pfn_info *page;
unsigned long pde;
#include <asm/domain_page.h>
#include <asm/pdb.h>
+extern void arch_init_memory(void);
extern void init_IRQ(void);
extern void trap_init(void);
extern void time_init(void);
time_init(); /* installs software handler for HZ clock. */
init_apic_mappings(); /* make APICs addressable in our pagetables. */
+ arch_init_memory();
+
#ifndef CONFIG_SMP
APIC_init_uniprocessor();
#else
extern unsigned int alloc_new_dom_mem(struct domain *, unsigned int);
extern long arch_do_dom0_op(dom0_op_t *op, dom0_op_t *u_dom0_op);
-extern void arch_getdomaininfo_ctxt(struct domain *, full_execution_context_t *);
+extern void arch_getdomaininfo_ctxt(
+ struct domain *, full_execution_context_t *);
static inline int is_free_domid(domid_t dom)
{
struct domain *d;
- if ( dom >= DOMID_SELF )
+ if ( dom >= DOMID_FIRST_RESERVED )
return 0;
if ( (d = find_domain_by_id(dom)) == NULL )
}
/* Couldn't find a free domain id in 0..topdom, try higher. */
- for ( dom = topdom; dom < DOMID_SELF; dom++ )
+ for ( dom = topdom; dom < DOMID_FIRST_RESERVED; dom++ )
{
if ( is_free_domid(dom) )
{
domid_t dom;
dom = op->u.createdomain.domain;
- if ( (dom > 0) && (dom < DOMID_SELF) )
+ if ( (dom > 0) && (dom < DOMID_FIRST_RESERVED) )
{
ret = -EINVAL;
if ( !is_free_domid(dom) )
d->domain = dom_id;
d->processor = cpu;
d->create_time = NOW();
- /* Initialise the sleep_lock */
spin_lock_init(&d->sleep_lock);
memcpy(&d->thread, &idle0_task.thread, sizeof(d->thread));
+ spin_lock_init(&d->page_alloc_lock);
+ INIT_LIST_HEAD(&d->page_list);
+ d->max_pages = d->tot_pages = 0;
+
+ /* Per-domain PCI-device list. */
+ spin_lock_init(&d->pcidev_lock);
+ INIT_LIST_HEAD(&d->pcidev_list);
+
if ( d->domain != IDLE_DOMAIN_ID )
{
if ( init_event_channels(d) != 0 )
d->addr_limit = USER_DS;
- spin_lock_init(&d->page_alloc_lock);
- INIT_LIST_HEAD(&d->page_list);
- d->max_pages = d->tot_pages = 0;
-
arch_do_createdomain(d);
- /* Per-domain PCI-device list. */
- spin_lock_init(&d->pcidev_lock);
- INIT_LIST_HEAD(&d->pcidev_list);
-
sched_add_domain(d);
write_lock_irqsave(&tasklist_lock, flags);
start_of_day();
- /* Add CPU0 idle task to the task hash list */
- task_hash[TASK_HASH(IDLE_DOMAIN_ID)] = &idle0_task;
-
/* Create initial domain 0. */
new_dom = do_createdomain(0, 0);
if ( new_dom == NULL )
unsigned long frame_table_size;
unsigned long max_page;
-extern void arch_init_memory(void);
-
void __init init_frametable(void *frametable_vstart, unsigned long nr_pages)
{
- unsigned long mfn;
-
- arch_init_memory();
-
max_page = nr_pages;
frame_table_size = nr_pages * sizeof(struct pfn_info);
frame_table_size = (frame_table_size + PAGE_SIZE - 1) & PAGE_MASK;
panic("Not enough memory for frame table - reduce Xen heap size?\n");
memset(frame_table, 0, frame_table_size);
-
- /* Initialise to a magic of 0x55555555 so easier to spot bugs later. */
- memset(machine_to_phys_mapping, 0x55, 4<<20);
-
- /* Pin the ownership of the MP table so that DOM0 can map it later. */
- for ( mfn = virt_to_phys(&machine_to_phys_mapping[0<<20])>>PAGE_SHIFT;
- mfn < virt_to_phys(&machine_to_phys_mapping[1<<20])>>PAGE_SHIFT;
- mfn++ )
- {
- frame_table[mfn].u.inuse.count_info = 1 | PGC_allocated;
- frame_table[mfn].u.inuse.type_info = 1 | PGT_gdt_page; /* non-RW */
- frame_table[mfn].u.inuse.domain = &idle0_task;
- }
}
}
if ( unlikely(d->tot_pages == 0) )
- get_domain(d);
+ get_knownalive_domain(d);
d->tot_pages += 1 << order;
/* _dom holds an allocation reference */ \
(_pfn)->u.inuse.count_info = PGC_allocated | 1; \
if ( unlikely((_dom)->xenheap_pages++ == 0) ) \
- get_domain(_dom); \
+ get_knownalive_domain(_dom); \
spin_unlock(&(_dom)->page_alloc_lock); \
} while ( 0 )
* ptr[1:0] == MMU_NORMAL_PT_UPDATE:
* Updates an entry in a page table. If updating an L1 table, and the new
* table entry is valid/present, the mapped frame must belong to the FD, if
- * an FD has been specified. If attempting to map an I/O page, then the FD
- * is ignored, but the calling domain must have sufficient privilege.
+ * an FD has been specified. If attempting to map an I/O page then the
+ * caller assumes the privilege of the FD.
+ * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller.
+ * FD == DOMID_XEN: Map restricted areas of Xen's heap space.
* ptr[:2] -- Machine address of the page-table entry to modify.
* val -- Value to write.
*
* val[7:0] == MMUEXT_SET_FOREIGNDOM:
* val[31:15] -- Domain to set as the Foreign Domain (FD).
* (NB. DOMID_SELF is not recognised)
+ * If FD != DOMID_IO then the caller must be privileged.
*
* val[7:0] == MMUEXT_REASSIGN_PAGE:
* ptr[:2] -- A machine address within the page to be reassigned to the FD.
#ifndef __ASSEMBLY__
typedef u16 domid_t;
+
+/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
+#define DOMID_FIRST_RESERVED (0x7FF0U)
+
/* DOMID_SELF is used in certain contexts to refer to oneself. */
-#define DOMID_SELF (0x7FF0U)
-/* NB. IDs >= 0x7FF1 are reserved for future use. */
+#define DOMID_SELF (0x7FF0U)
+
+/*
+ * DOMID_IO is used to restrict page-table updates to mapping I/O memory.
+ * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO
+ * is useful to ensure that no mappings to the OS's own heap are accidentally
+ * installed. (e.g., in Linux this could cause havoc as reference counts
+ * aren't adjusted on the I/O-mapping code path).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can
+ * be specified by any calling domain.
+ */
+#define DOMID_IO (0x7FF1U)
+
+/*
+ * DOMID_XEN is used to allow privileged domains to map restricted parts of
+ * Xen's heap space (e.g., the machine_to_phys table).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if
+ * the caller is privileged.
+ */
+#define DOMID_XEN (0x7FF2U)
/*
* Send an array of these to HYPERVISOR_mmu_update().
#define DOMAIN_DESTRUCTED (1<<31) /* assumes atomic_t is >= 32 bits */
#define put_domain(_d) \
if ( atomic_dec_and_test(&(_d)->refcnt) ) domain_destruct(_d)
+
+/*
+ * Use this when you don't have an existing reference to @d. It returns
+ * FALSE if @d is being destructed.
+ */
static inline int get_domain(struct domain *d)
{
atomic_inc(&d->refcnt);
return !(atomic_read(&d->refcnt) & DOMAIN_DESTRUCTED);
}
+
+/*
+ * Use this when you already have, or are borrowing, a reference to @d.
+ * In this case we know that @d cannot be destructed under our feet.
+ */
+static inline void get_knownalive_domain(struct domain *d)
+{
+ atomic_inc(&d->refcnt);
+ ASSERT(!(atomic_read(&d->refcnt) & DOMAIN_DESTRUCTED));
+}
extern struct domain *do_createdomain(
domid_t dom_id, unsigned int cpu);